package game

import (
	"time"
	"github.com/name5566/leaf/gate"
	"github.com/name5566/leaf/go"
	"github.com/name5566/leaf/log"
	"github.com/name5566/leaf/timer"
	"db"
	"message"
	"message/netlib"
	"fmt"
	"game/poker/hand"
	"github.com/golang/protobuf/proto"
)

var (
	account2Users = make(map[string]*User)    // 玩家账户名到玩家对象的映射表
	users = make(map[int]*User)    // 玩家列表
)

// 玩家
type User struct {
	gate.Agent                          // 玩家的客户端连接（网关服务器维护）
	*g.LinearContext                    // 玩家自己的协程，不会受其他玩家影响
	onlineStatus int                    // 玩家的在线状态
	data         *db.UserData           // 玩家的持久化数据
	saveDBTimer  *timer.Timer           // 自动持久化的计时器
	token        string                 // 玩家有效性的token，由登录服务器生成，持有此token的玩家才是合法玩家


	playerID     int                    // 玩家id
	hallID       int                    // 玩家所在大厅的ID
	roomID       int                    // 玩家所在房间的ID
	room         *Room                  // 所在房间
	bot          bool                   // 是否是机器人
	seat         int                    // 当前座位 -1代表没有入座
	money           int                 // 玩家携带的筹码
	totalMoneyBegin int64               // 记录一下玩家开始玩的时候的总金钱，在玩家退出到大厅的时候提示赢了或者输了多少
	handCard1       *hand.Card          // 手牌
	handCard2       *hand.Card          // 手牌2
	thinkTime       int                 // 思考的倒计时
	state           message.PlayerState // 玩家状态
	action          message.PokerAction // 玩家当前的操作
	actionParam     int                 //对应的操作参数
	autoFold        bool                // 自动弃牌
	autoCheckOrFold bool                // 动过牌或弃牌（反正不花钱，能过就过，不能过就弃）
	autoCall        bool                // 自动跟注
	autoCallParam   int                 // 自动跟注的跟注金额
	autoCallAny     bool                // 自动跟任何注（只要有钱就跟，没钱就弃牌）
	autoReBuy       bool                // 筹码不够的时候自动买入
	autoShowCard    bool                // 结束时自动亮牌
	raiseCount      int                 // 加注的次数
	bet             int                 // 押注(仅仅同步给客户端使用，考虑到玩家离席的情况，服务器不要使用这个字段)
	sidePoolIndex   int                 // 边池的索引
}

// 用于打印log时标识玩家
func (self *User) str() string {
	return fmt.Sprintf("%v(%v)", self.data.Name, self.seat)
}

func (self *User) onInit() {
	if self.data.Name == "" {
		self.data.Name = fmt.Sprintf("Player%d", self.data.UserID)
	}

	self.hallID = 0
	self.roomID = 0
	self.room = nil
	self.bot = false
	self.seat = -1

	self.money = 0
	self.totalMoneyBegin = 0
	self.handCard1 = nil
	self.handCard2 = nil
	self.thinkTime = 0
	self.state = message.PlayerState_STATE_STAND
	self.action = message.PokerAction_ACTION_NONE
	self.actionParam = 0
	self.autoFold = false
	self.autoCheckOrFold = false
	self.autoCall = false
	self.autoCallParam = 0
	self.autoCallAny = false
	self.autoReBuy = false
	self.autoShowCard = false
	self.raiseCount = 0
	self.bet = 0
	self.sidePoolIndex = -1
}

func (self *User) ID() int {
	return self.data.UserID
}

// 玩家登录
func (self *User) login(account string, token string) {
	userData := db.FindUser(account)
	if userData == nil {
		// 某些游戏需要创建角色   德州扑克自动创建角色
		userData = db.CreateUser(account)
	}

	// 如果此账号的玩家已经登录 正常情况下是不会走到这个分支的，上层已经判断
	if oldUser, ok := account2Users[account]; ok {
		log.Error("玩家已登录  old:%v    new:%v", oldUser.data, userData)
		return
	}

	self.data = userData
	self.onlineStatus = USER_STATE_ONLINE
	users[userData.UserID] = self
	account2Users[account] = self

	// 处理玩家登录（游戏逻辑）
	self.onLogin()

	// 存档
	self.autoSaveDB()

	log.Debug("玩家登陆成功: %v  %v", account, token)
}

// 玩家登出
func (self *User) logout() {
	if self.data == nil {
		log.Error("logout 无效的玩家数据")
		return
	}

	self.reqStopGame()

	if self.bot {
		return
	}

	// 停止保存玩家的计时器
	if self.saveDBTimer != nil {
		self.saveDBTimer.Stop()
	}

	// 处理玩家登出(游戏逻辑)
	self.onLogout()

	// 保存玩家数据
	db.SaveUser(self.data)

	log.Debug("玩家登出成功: %v", self.data.Account)

	// 清理数据
	delete(users, self.data.UserID)
	delete(account2Users, self.data.Account)
}

// 自动保存玩家数据
func (self *User) autoSaveDB() {
	const duration = 5 * time.Minute
	// save
	self.saveDBTimer = skeleton.AfterFunc(duration, func() {
		self.Go(func() {
			db.SaveUser(self.data)
		}, func() {
			self.autoSaveDB()
		})
	})
}

// 玩家是否在线
func (self *User) isOnline() bool {
	return self.onlineStatus == USER_STATE_ONLINE
}

// 玩家是否离线
func (self *User) isOffline() bool {
	return self.onlineStatus != USER_STATE_ONLINE
}

// 发送消息
func (self *User) SendMsg(msgID message.MSG, msg interface{}) {
	if self.Agent != nil {
		log.Debug("SendMsg:  %v    %v", msgID, msg)
		netlib.SendMsg(self.Agent, msgID, msg)
	}  else {
		//log.Error("SendMsg no agent:  %v", self.data.Account)
	}
}

func (self *User) SendError(msgID message.MSG, errCode message.ErrorCode) {
	if self.Agent != nil {
		netlib.SendError(self.Agent, msgID, errCode)
	} else {
		log.Error("SendError no agent:  %v", self.data.Account)
	}
}

func (self *User) SendErrorMsg(msgID message.MSG, errCode message.ErrorCode, errMsg string) {
	if self.Agent != nil {
		netlib.SendErrorMsg(self.Agent, msgID, errCode, errMsg)
	}  else {
		log.Error("SendErrorMsg no agent:  %v", self.data.Account)
	}
}

func (self *User) getUserInfo() *message.UserInfo {
	return &message.UserInfo{
		EntityID:proto.Int(self.data.UserID),
		Name:proto.String(self.data.Name),
		Icon:proto.String(self.data.Icon),
		TotalMoney:proto.Int64(self.data.TotalMoney),
		TotalMatch:proto.Int32(self.data.TotalCount),
		WinMatch:proto.Int32(self.data.WinCount),
	}
}
